package cn.ddiancan.xddcloud.psc.permission.aspect;

import cn.ddiancan.xddcloud.common.context.RequestContext;
import cn.ddiancan.xddcloud.common.entity.PermissionVO;
import cn.ddiancan.xddcloud.common.entity.UserVO;
import cn.ddiancan.xddcloud.common.exception.ServiceException;
import cn.ddiancan.xddcloud.psc.permission.annotation.UserOperation;
import cn.ddiancan.xddcloud.psc.permission.annotation.UserResource;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Objects;

@Aspect
@Component
public class PermissionAspect {

    @Pointcut("@annotation(cn.ddiancan.xddcloud.psc.permission.annotation.UserOperation)")
    public void pointCut() {

    }

    @Around("pointCut()")
    public Object arround(ProceedingJoinPoint joinPoint) throws Throwable {
        RequestContext requestContext = RequestContext.getRequestContext(false);
        checkUserLogin(requestContext);
        UserVO user = RequestContext.getCurrentContextUser();
        Collection<String> authorities = user.getUserPermissions();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        UserResource resource = method.getDeclaringClass().getAnnotation(UserResource.class);
        UserOperation userOperation = method.getAnnotation(UserOperation.class);
        if (resource != null && userOperation != null) {
            String resourceCode = resource.code();
            String operationCode = userOperation.code();
            String permissionPoint =
                new StringBuilder("Service$").append(resourceCode).append("$").append(operationCode).toString();
            if (!authorities.contains(permissionPoint) && !authorities.contains("ALL")) {
                // TODO当前用户为登录用户，考虑缓存问题，此处应当再重新走库重查一次
                throw new ServiceException(HttpStatus.UNAUTHORIZED.value(),
                    "Access denied,The user " + user.getUserName() + " has no permission.[" + permissionPoint + "]");
            }
        }
        method.setAccessible(true);
        return joinPoint.proceed();
    }

    private void checkUserLogin(RequestContext requestContext) {
        if (Objects.isNull(requestContext) || Objects.isNull(requestContext.getCurrentUser()) ||
            !StringUtils.hasText(((UserVO) requestContext.getCurrentUser()).getUserName())) {
            throw new ServiceException(HttpStatus.UNAUTHORIZED.value(), "No login,Please do login first.");
        }
    }
}
